﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UniUI.ConsoleUI
{
    public class ConsoleScreen
    {
        private readonly int width, height;
        private int maskL, maskT, maskR, maskB;

        internal ConsoleScreen() :
            this(Console.WindowWidth, Console.WindowHeight)
        {
        }

        internal ConsoleScreen(int width, int height)
        {
            this.width = width;
            this.height = height;
            SetMask(0, 0, width, height);

            BackgroundColor = ConsoleColor.Black;
            DefaultBackgroundColor = ConsoleColor.Black;
            ForegroundColor = ConsoleColor.Gray;
            DefaultForegroundColor = ConsoleColor.Gray;
        }

        public int Width { get { return width; } }
        public int Height { get { return height; } }

        public ConsoleColor BackgroundColor { get; set; }
        public ConsoleColor ForegroundColor { get; set; }
        public ConsoleColor DefaultBackgroundColor { get; set; }
        public ConsoleColor DefaultForegroundColor { get; set; }

        public int FocusX { get; set; }
        public int FocusY { get; set; }
        public bool ShowFocus { get; set; }

        public void SetMask(int l, int t, int r, int b)
        {
            this.maskL = l;
            this.maskT = t;
            this.maskR = r;
            this.maskB = b;
        }

        public void DrawRect()
        {
            DrawRect(0, 0, Width, Height);
        }

        public void DrawRect(int l, int t, int w, int h)
        {
            int drawL = Math.Max(l, maskL),
                drawT = Math.Max(t, maskT),
                drawR = Math.Min(l + w, maskR),
                drawB = Math.Min(t + h, maskB);
            Console.BackgroundColor = BackgroundColor;
            for (int i = drawT; i < drawB; ++i)
            {
                Console.SetCursorPosition(drawL, i);
                Console.Write(new string(' ', drawR - drawL));
            }
        }

        public void DrawString(int l, int t, string str)
        {
            Console.BackgroundColor = BackgroundColor;
            Console.ForegroundColor = ForegroundColor;
            if (t < maskT || t > maskB || l >= maskR)
            {
                return;
            }
            int index = 0;
            if (l < maskL)
            {
                if (str.Length <= maskL - l)
                {
                    return;
                }
                index = maskL - l;
                l = maskL;
            }
            Console.SetCursorPosition(l, t);
            while (index < str.Length && Console.CursorLeft < maskR)
            {
                Console.Write(str[index++]);
            }
        }

        public void Clear()
        {
            //TODO consider Console.Clear
            Console.BackgroundColor = DefaultBackgroundColor;

            //only to height - 1 (avoid writing to the last line, which might be used by input method)
            //TODO will produce wrong result if, while rendering the last line, the input method is activated
            for (int i = 0; i < Height - 1; ++i)
            {
                Console.SetCursorPosition(0, i);
                Console.Write(new string(' ', Width));
            }
        }

        public void Present()
        {
            if (ShowFocus)
            {
                Console.SetCursorPosition(FocusX, FocusY);
                Console.CursorVisible = true;
            }
            else
            {
                Console.CursorVisible = false;
            }

            //sometimes this operation is not supported (when it's always 0)
            try
            {
                Console.WindowTop = 0;
            }
            catch
            {
            }

            Console.BackgroundColor = DefaultBackgroundColor;
            Console.ForegroundColor = DefaultForegroundColor;
        }

        private static Dictionary<char, int> charLengthCache = new Dictionary<char, int>();

        private static int GetCharLength(char c)
        {
            if (charLengthCache.ContainsKey(c))
            {
                return charLengthCache[c];
            }

            //use left top corner to draw character
            Console.SetCursorPosition(0, 0);
            Console.Write(c);
            var ret = Console.CursorLeft;
            charLengthCache.Add(c, ret);

            //force refresh the region
            //TODO 3 is enough?
            ConsoleUIManager.Invalidate(0, 0, 3, 1);

            return ret;
        }

        public static int GetStringLength(string str)
        {
            return str.Sum(c => GetCharLength(c));
        }

        public static int GetStringLength(string str, int len)
        {
            int ret = 0;
            for (int i = 0; i < len && i < str.Length; ++i)
            {
                ret += GetCharLength(str[i]);
            }
            return ret;
        }

        //not tested
        public static string TruncateString(string str, int max)
        {
            int len = 0, pos = 0;
            while (pos < str.Length)
            {
                var charLen = GetCharLength(str[pos]);
                if (len + charLen > max)
                {
                    break;
                }
                len += charLen;
                pos += 1;
            }
            return str.Substring(0, pos);
        }
    }
}
